#include "sdio.h"

sdio_state_t g_sdio_state = SDIO_STATE_UNSPEC;


/* grab a buffer from the interrupt in queue ring-buffer */
static struct lark_resp *get_resp_buf(void)
{
	struct lark_resp *tmp;

	if (fw.sdio.resp_pending == NUM_SDIO_RESP_BUFS) {
		printk("no more buf for sdio resp\n");
		return NULL;
	}

	/* fetch the _oldest_ buffer from the ring */
	tmp = &fw.sdio.resp_buf[fw.sdio.resp_tail_index];

	fw.sdio.resp_tail_index++;
	fw.sdio.resp_tail_index %= NUM_SDIO_RESP_BUFS;
	if (fw.sdio.resp_pending != NUM_SDIO_RESP_BUFS)
		fw.sdio.resp_pending++;

	return tmp;
}

static lark_resp *dequeue_resp_buf(void)
{
	struct lark_resp *tmp = NULL;
	if (fw.sdio.resp_pending != 0) {
		tmp = &fw.sdio.resp_buf[fw.sdio.resp_head_index];

		fw.sdio.resp_head_index++;
		fw.sdio.resp_head_index %= NUM_SDIO_RESP_BUFS;
		fw.sdio.resp_pending--;
	}

	return tmp;
}

/* grab a buffer from the interrupt in queue ring-buffer */
static struct lark_data *get_in_data_buf(void)
{
	struct lark_data *tmp;

	if (fw.sdio.in_pending == LARK_DATA_LEN) {
		printk("no more buf for sdio in\n");
		return NULL;
	}

	/* fetch the _oldest_ buffer from the ring */
	tmp = &fw.sdio.in_buf[fw.sdio.in_tail_index];

	fw.sdio.in_tail_index++;

	fw.sdio.in_tail_index %= LARK_DATA_LEN;
	if (fw.sdio.in_pending != LARK_DATA_LEN)
		fw.sdio.in_pending++;

	return tmp;
}

static lark_resp *dequeue_in_data_buf(void)
{
	struct lark_data *tmp = NULL;
	if (fw.sdio.in_pending != 0) {
		tmp = &fw.sdio.in_buf[fw.sdio.in_head_index];

		fw.sdio.in_head_index++;
		fw.sdio.in_head_index %= LARK_DATA_LEN;
		fw.sdio.in_pending--;
	}

	return tmp;
}

void process_sdio(void)
{
	int ret;
	u16 int_status;
	void *buf_add;
	u8 *mac_addr;
	u8 temp;
	int i;

	if (g_sdio_state != SDIO_STATE_IO_ENABLED) {
		int_status = get_sdio_int_status();
		if (int_status & HOST_RESET) {
			clr_sdio_int_pnd(HOST_RESET);
		}
	}

	switch (g_sdio_state) {
	case SDIO_STATE_UNSPEC:

	    clr_sdio_int_pnd(0xFFFF);
	    set_sdio_int_msk(~HOST_RESET);	/* 0xF800 ? */
	    g_sdio_state = SDIO_STATE_IDLE;
	    printk("changing sdio state to IDLE\n");
		break;

	case SDIO_STATE_IDLE:
		if (getb(SDIO_STATE_CONTROL_REG) & START_CARD_INIT_EN) {
			g_sdio_state = SDIO_STATE_CMD5_RECVD;
			printk("changing sdio state to CMD5_REVD\n");
		}
		break;

	case SDIO_STATE_CMD5_RECVD:
		/* Set the Card Ready Flar is SD State Control register */
		set_sdio_card_ready_flag();
		
		/* Initialize the burst size */
		set_sdio_chnl_len(SDIO_CHNL_LEN_VAL);	/* 16 */
		
		/* Initialize Rx Path */
		buf_add = get_in_data_buf();
		/* set Buffer addr to rx buffer */
		put_rx_sdio_hw_q(buf_add);

		/* Only failure DMA Interrupts are unmasked */
		clr_sdio_dma_int_pnd(0xFF);
		set_sdio_dma_int_msk(0x11);
		
		/* Unmask all interrupts */
		clr_sdio_int_pnd(0xFFFF);
		set_sdio_int_msk(0xF800);

		set_sdio_ctrl_reg(0x22);	// ahb word mode
	
		/*
		 * DMA Timeout is set to 1ms
		 * TODO: modify these values to adjust to our platorm !!!
		 * This value use AHB clock to determine its value.
		 */
		set_sdio_dma_timeout(0x2);		// set timout register,SDIO_BASE + 0x00000011
		sdio_timer_term_cnt(0xC350);	// set timer termination counter,+0x0000002E,
										// 50,000/100MHz => 0.5ms	   
										// 2*0.5ms = 1ms	
		printk("Waiting for IO enable....\n");
		printk("changing sdio state to WAIT_FOR_IE\n");
		g_sdio_state = SDIO_STATE_WAIT_FOR_IE;
		break;

	case SDIO_STATE_WAIT_FOR_IE:
		temp = sdio_read_sd_reg((u32)(rSDIO_IO_ENABLE));
		if (temp & BIT(1)) {
			/* Finalizing the initialization with the IOReady for function 1 */
		    sdio_write_sd_reg(rSDIO_IO_READY, 0x02);
			g_sdio_state = SDIO_STATE_IO_ENABLED;
		}
		break;

	case SDIO_STATE_IO_ENABLED:
		// do data transfer, etc.
		process_sdio_tx_to_host();
		break;

	default:
		printk("XXX Invalid sdio state\n");
		break;
	}
}

int sdio_send_packet(u8* data, u32 length)
{
    u16 dwords = 0;
    u16 ahb_status;

	ahb_status = getw(SDIO_INT_STATUS);
	if (ahb_status != 0) {
		return -1;
	}

    dwords = (length + 3 ) / 4;
    put_tx_sdio_hw_q((u32)(data), dwords);

    return 0;
}

void sdio_receive(void)
{
    u8     *rx_buf     = 0;
    u16    rx_length   = 0;
    u8     *new_rx_buf = 0;
    u8     type;
    u8	   offset = 0;

    /* Get the rx buffer addres from rxdma buffer que */
    rx_buf = (u8*)get_rx_sdio_hw_q();
    if (!rx_buf) {
    	printk("rx_buf is 0\n");
    	return;
    }

	/* TODO */
}


